//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------
/*
*/
package uxgenes.core;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Scanner;

import uxgenes.app.R;
import junit.framework.Assert;
import android.app.ActivityManager;
import android.app.ActivityManager.MemoryInfo;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.AssetManager;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Vibrator;
import android.provider.Settings.Secure;
import android.telephony.SmsManager;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.Toast;


//
// Engine Context
//
public final class EngineContext implements	SurfaceHolder.Callback,
											View.OnKeyListener,
											View.OnTouchListener,
											SensorEventListener,
											LocationListener,
											Runnable
{
// types //
	public interface ActivityInterface
	{
		public abstract void Close ();
		public abstract void SetRequestedOrientation (int orient);
	}
	
	public interface EventListener
	{
		public abstract void OnProcessEvent (int event, int param0, int param1, int param2);
	}
	
	
// constants //
	private static final String		TAG = "BaseEngineContext";

	private static final int		SENSOR_ACCELEROMETER	= 1;
	private static final int		SENSOR_MAGNETIC_FIELD	= 2;
	private static final int		SENSOR_GYROSCOPE		= 3;
	private static final int		SENSOR_ROTATION			= 4;
	private static final int		SENSOR_ORIENTATION		= 5;
	private static final int		SENSOR_LIGHT			= 6;
	private static final int		SENSOR_PROXIMITY		= 7;
	private static final int		SENSOR_LOCATION			= 8;
	
	private static final int		EXT_CAMERA	= 1;


// variables //
	public NativeLib			nativeLib;
	
	private final Context			_context;
	private final ActivityInterface	_activityInterface;

	private SurfaceView	 		_view;
	private View				_rootView;
	private Handler 			_handler;
	private AssetManager 		_assetManager;
	private boolean				_backgroundModeEnabled = false;
	private boolean				_isVisible = false;
	
	private OnAudioFocusChangeListener	_audioFocusListener = null;

	// sensors
	private SensorManager		_sensorManager		= null;
	private int					_enabledSensorBits	= 0;
	private int					_savedSensorBits	= 0;
	private boolean				_sensorsEnabled		= false;
	private float[]				_gravity			= null;
	private float[]				_magnetic			= null;
	
	// touches
	private static final int	MAX_TOUCHES 	= 6;
	private static final int	DATA_PER_TOUCH 	= 4;	// id, x, y, pressure
	private float				_touchesData[] 	= new float[MAX_TOUCHES * DATA_PER_TOUCH];
	private float				_height			= 0;
	
	// extensions
	private HashSet< uxgenes.ext.IExtension >	_extensions = new HashSet< uxgenes.ext.IExtension >();
	
	// #ifdef CAMERA
	public uxgenes.ext.camera.UXCamera	camera = null;
	// #endif CAMERA
	
	// events
	private final ArrayList<EventListener>	_listeners = new ArrayList< EventListener >();
	

	
// methods //
	
	// constructor
	public EngineContext (Context ctx, ActivityInterface cb)
	{
		this._context = ctx;
		this._activityInterface = cb;
		
		Assert.assertNotNull( cb );
		
		UXUtils.CheckDebuggable( _context );
	}
	
	
	// InitEngine
	public final void InitEngine (boolean requireExternalStorage)
	{
		File 	internalPath	= _context.getFilesDir();
		File	internalCache	= _context.getCacheDir();
		File	externalPath	= _context.getExternalFilesDir(null);
		File	externalCache	= _context.getExternalCacheDir();
		File	externalStorage	= Environment.getExternalStorageDirectory();
		
		if ( requireExternalStorage && externalPath == null )
		{
			AlertDialog.Builder builder = new AlertDialog.Builder( _context );
			builder.setTitle( _context.getString( R.string.txt_error ) );
			builder.setMessage( _context.getString( R.string.txt_sd_card_not_available ) );
			builder.setCancelable( false );
			builder.setPositiveButton( "OK", new DialogInterface.OnClickListener()
			{
				public void onClick (DialogInterface arg0, int arg1) {
					_activityInterface.Close();
				}
			});
			builder.create().show();
		}
		
		nativeLib = new NativeLib( this );
        
        _sensorManager = (SensorManager)_context.getSystemService( Context.SENSOR_SERVICE );

		_assetManager = _context.getResources().getAssets();
		nativeLib.OnInit( _assetManager, _GetSupportedSensorsBits() );
		_SendDeviceInfo();
		
		nativeLib.SetDirectory(
				( internalPath    == null ? "" : internalPath.getAbsolutePath()    ),
				( internalCache   == null ? "" : internalCache.getAbsolutePath()   ),
				( externalPath    == null ? "" : externalPath.getAbsolutePath()    ),
				( externalCache   == null ? "" : externalCache.getAbsolutePath()   ),
				( externalStorage == null ? "" : externalStorage.getAbsolutePath() )
		);

        Logger.I( TAG, "Engine initialized" );
	}


	// CreateEngine
	public final void CreateEngine (ViewGroup rootView, int viewId)
	{
		if ( nativeLib == null ) {
			Logger.E( TAG, "Engine must be initialized before creating surface" );
			return;
		}
		
		_view = new SurfaceView( _context );
		SurfaceHolder	holder = _view.getHolder();
		
		holder.addCallback( this );
		holder.setFormat( android.graphics.PixelFormat.RGBA_8888 );
		
        _view.setFocusable( true );
        _view.setFocusableInTouchMode( true );
        _view.requestFocus();
        _view.setOnKeyListener( this ); 
        _view.setOnTouchListener( this );
        
        if ( rootView != null )
        {
        	ViewGroup gr = (ViewGroup) rootView.findViewById( viewId );
        	gr.addView( _view, new LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT ) );
        	_rootView = rootView;
        }
        else {
        	_rootView = _view;
        }
        
        try {
	        for (uxgenes.ext.IExtension e : _extensions) {
	        	e.OnInit();
	        }
        }
        catch (Exception e) {
        	Logger.OnCatchException( TAG, e );
        }
        
        Logger.I( TAG, "Engine created" );
	}


	// CreateEngineForWallpaper
	public final void CreateEngineForWallpaper ()
	{
		if ( nativeLib == null ) {
			Logger.E( TAG, "Engine must be initialized before creating surface" );
			return;
		}
		
		_view = null;
        
        try {
	        for (uxgenes.ext.IExtension e : _extensions) {
	        	e.OnInit();
	        }
        }
        catch (Exception e) {
        	Logger.OnCatchException( TAG, e );
        }
        
        Logger.I( TAG, "Engine created" );
	}
	
	
	// CloseEngine
	public final void CloseEngine ()
	{
		_activityInterface.Close();
	}
	
	
// Activity Events //
	
	// OnDestroy
	public final void OnDestroy ()
	{
        try {
	        for (uxgenes.ext.IExtension e : _extensions) {
	        	e.OnDestroy();
	        }
        }
        catch (Exception e) {
        	Logger.OnCatchException( TAG, e );
        }
        
		if ( nativeLib != null ) {
			nativeLib.OnQuit();
			nativeLib = null;
		}
		
		_sensorManager = null;
        
		Logger.I( TAG, "OnDestroy" );
	}

	
	// OnPause
	public final void OnPause ()
	{
		Logger.I( TAG, "OnPause" );
		
		if ( nativeLib != null ) {
			nativeLib.OnPause();
		}

        try {
	        for (uxgenes.ext.IExtension e : _extensions) {
	        	e.OnPause();
	        }
        }
        catch (Exception e) {
        	Logger.OnCatchException( TAG, e );
        }
	}

	
	// OnResume
	public final void OnResume ()
	{
		Logger.I( TAG, "OnResume" );
		
		if ( nativeLib != null ) {
			nativeLib.OnResume();
		}

        try {
	        for (uxgenes.ext.IExtension e : _extensions) {
	        	e.OnResume();
	        }
        }
        catch (Exception e) {
        	Logger.OnCatchException( TAG, e );
        }
	}
	
	
	// OnStart
	public final void OnStart ()
	{
		Logger.I( TAG, "OnStart" );
		
		if ( nativeLib != null )
		{
			nativeLib.OnStart();
	
			_handler = new Handler();
			_handler.post( this );
		}

        try {
	        for (uxgenes.ext.IExtension e : _extensions) {
	        	e.OnStart();
	        }
        }
        catch (Exception e) {
        	Logger.OnCatchException( TAG, e );
        }
	}

	
	// OnStop
	public final void OnStop ()
	{
		Logger.I( TAG, "OnStop" );
		
		if ( nativeLib != null ) {
			nativeLib.OnStop();
		}
		if ( !_backgroundModeEnabled )
			_handler = null;

        try {
	        for (uxgenes.ext.IExtension e : _extensions) {
	        	e.OnStop();
	        }
        }
        catch (Exception e) {
        	Logger.OnCatchException( TAG, e );
        }
	}

	
	// OnLowMemory
    public final void OnLowMemory ()
    {
		Logger.W( TAG, "OnLowMemory" );
        
        if ( nativeLib != null ) {
        	nativeLib.LowMemory();
        }
    }
    
    
    // OnWindowFocusChanged
    public final void OnWindowFocusChanged (boolean hasFocus)
    {
		Logger.I( TAG, "OnWindowFocusChanged: " + hasFocus );
		
    	_isVisible = hasFocus;
    	
    	if ( nativeLib != null ) {
    		nativeLib.VisibilityChanged( hasFocus );
    	}
    }

	
	
// SurfaceHolder.Callback //

	// surfaceChanged
	public final void surfaceChanged (SurfaceHolder holder, int format, int w, int h)
	{
		Logger.I( TAG, "surfaceChanged" );
		
		nativeLib.OnSurfaceChanged( holder.getSurface(), w, h );
		_height = (float) h;
	}

	
	// surfaceCreated
	@SuppressWarnings("deprecation")
	public final void surfaceCreated (SurfaceHolder holder)
	{
		Logger.I( TAG, "surfaceCreated" );
		
        holder.setType( SurfaceHolder.SURFACE_TYPE_GPU );
        _EnableSensors( true );
	}

	
	// surfaceDestroyed
	public final void surfaceDestroyed (SurfaceHolder holder)
	{
		Logger.I( TAG, "surfaceDestroyed" );

		_EnableSensors( false );
		
		if (nativeLib != null) {
			nativeLib.OnSurfaceDestroyed();
		}
	}

		
		
// SensorEventListener //
	
	// onAccuracyChanged
	public final void onAccuracyChanged (Sensor arg0, int arg1)
	{
		Logger.I( TAG, "onAccuracyChanged" );
	}

	
	// onSensorChanged
	public final void onSensorChanged (SensorEvent ev)
	{
		//Logger.I( TAG, "onSensorChanged" );
		
		int	sensor = ev.sensor.getType();
		
		switch ( sensor )
		{
			case Sensor.TYPE_ACCELEROMETER :
				nativeLib.OnAcceleration( ev.values[0], ev.values[1], ev.values[2] );
				_gravity = ev.values.clone();
				_OrientChanged();
				break;
				
			case Sensor.TYPE_MAGNETIC_FIELD :
				nativeLib.OnMagneticField( ev.values[0], ev.values[1], ev.values[2] );
				_magnetic = ev.values.clone();
				_OrientChanged();
				break;
				
			case Sensor.TYPE_GYROSCOPE :
				nativeLib.OnGyroscope( ev.values[0], ev.values[1], ev.values[2] );
				break;
				
			case Sensor.TYPE_ORIENTATION :
				//nativeLib.OnOrientation( ev.timestamp, ev.values[0], ev.values[1], ev.values[2] );
				break;
				
			case Sensor.TYPE_ROTATION_VECTOR :
				nativeLib.OnRotation( ev.values[0], ev.values[1], ev.values[2] );
				break;
				
			case Sensor.TYPE_LIGHT :
				nativeLib.OnLightSensor( ev.values[0] );
				break;
				
			case Sensor.TYPE_PROXIMITY :
				nativeLib.OnProximitySensor( ev.values[0] );
				break;
		}
	}
	
	
	// _OrientChanged
	private final void _OrientChanged ()
	{
		if ( _gravity != null && _magnetic != null && _IsSensorEnabled( SENSOR_ORIENTATION ) )
		{
			float R[] = new float[9];
			float I[] = new float[9];
			
			boolean success = SensorManager.getRotationMatrix( R, I, _gravity, _magnetic );
			if (success) {
				float orient[] = new float[3];
				SensorManager.getOrientation( R, orient );
				nativeLib.OnOrientation( orient[0], orient[1], orient[2] );
			}
		}
	}

		
// Sensors //
	
	// _SetSensorState
	private final boolean _SetSensorState (boolean enabled, int sensor, int delay)
	{
		try {
			if ( enabled ) {
				return _sensorManager.registerListener( this, _sensorManager.getDefaultSensor( sensor ), delay, null );
			} else {
				_sensorManager.unregisterListener( this, _sensorManager.getDefaultSensor( sensor ) );
				return true;
			}
		}
		catch (Exception e) {
			Logger.OnCatchException( TAG, e );
			return false;
		}
	}
	
	
	// _ConvertSensorType
	private final int _ConvertSensorType (int sensor)
	{
		switch ( sensor )
		{
			case SENSOR_ACCELEROMETER 	:	return Sensor.TYPE_ACCELEROMETER;
			case SENSOR_MAGNETIC_FIELD	:	return Sensor.TYPE_MAGNETIC_FIELD;
			case SENSOR_GYROSCOPE 		:	return Sensor.TYPE_GYROSCOPE;	
			case SENSOR_ROTATION		:	return Sensor.TYPE_ROTATION_VECTOR;
			case SENSOR_ORIENTATION		:	return Sensor.TYPE_ORIENTATION;
			case SENSOR_LIGHT 			:	return Sensor.TYPE_LIGHT;
			case SENSOR_PROXIMITY 		:	return Sensor.TYPE_PROXIMITY;
		}
		return 0;
	}
	
	
	// _ConvertASensorType
	private final int _ConvertASensorType (int sensor)
	{
		switch ( sensor )
		{
			case Sensor.TYPE_ACCELEROMETER		:	return SENSOR_ACCELEROMETER;
			case Sensor.TYPE_MAGNETIC_FIELD		:	return SENSOR_MAGNETIC_FIELD;
			case Sensor.TYPE_GYROSCOPE			:	return SENSOR_GYROSCOPE;
			case Sensor.TYPE_ROTATION_VECTOR	:	return SENSOR_ROTATION;
			case Sensor.TYPE_ORIENTATION		:	return SENSOR_ORIENTATION;
			case Sensor.TYPE_LIGHT				:	return SENSOR_LIGHT;
			case Sensor.TYPE_PROXIMITY			:	return SENSOR_PROXIMITY;
		}
		return 0;
	}

	
	// _IsSensorEnabled
	private final boolean _IsSensorEnabled (int sensor)
	{
		return ((_enabledSensorBits >> sensor) & 1) == 1;
	}
	
	
	// _SetSensorState
	private final void _SetSensorBits (int sensor, boolean enabled)
	{
		_enabledSensorBits ^= 1 << sensor;
		_enabledSensorBits |= (enabled ? 1 : 0) << sensor;
	}
	
	
	// _EnableSensors
	private final void _EnableSensors (boolean enable)
	{
		if ( _sensorsEnabled == enable )
			return;

		if ( !enable )
			_savedSensorBits = _enabledSensorBits;
		
		final int	sensors = _savedSensorBits;
		
		for (int i = 0; i < 32; ++i)
		{
			if ( ((sensors >> i) & 1) == 0 )
				continue;
			
			SetSensorState( i, enable, SensorManager.SENSOR_DELAY_GAME );
		}
		
		_sensorsEnabled = enable;
	}

	
	// SetSensorState
	public final boolean SetSensorState (int sensor, boolean enabled, int delay)
	{
		if ( !enabled && _IsSensorEnabled( sensor ) == enabled )
			return true;
		
		int 	type = _ConvertSensorType( sensor );
		boolean	res  = false;

		if ( sensor == SENSOR_LOCATION )
		{
			res = _SetLocationSensorState( enabled );
		}
		else
		if ( type != 0 )
		{
			res = _SetSensorState( enabled, type, delay );
		}

		if ( res )
			_SetSensorBits( sensor, enabled );
		
		return res;
	}
		
		
		
// LocationListener //
		
	// _SetLocationSensorState
	private final boolean _SetLocationSensorState (boolean enabled)
	{
		try
		{
			LocationManager locationManager = (LocationManager) _context.getSystemService( Context.LOCATION_SERVICE );
			
			if ( enabled ) {
				locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 0, 0, this );
			} else {
				locationManager.removeUpdates( this );
			}
			return true;
		}
		catch (Exception e)
		{
			Logger.OnCatchException( TAG, e );
		}
		return false;
	}
	
		
	// onLocationChanged
	public final void onLocationChanged (Location location)
	{
		nativeLib.OnLocation( location.getTime(), location.getLongitude(), location.getLatitude(),
							  (float) location.getAltitude(), location.getAccuracy(), location.getSpeed() );
	}
	
		
	// onProviderDisabled
	public final void onProviderDisabled (String provider)
	{
	}
	
		
	// onProviderEnabled
	public final void onProviderEnabled (String provider)
	{
	}
	
		
	// onStatusChanged
	public final void onStatusChanged (String provider, int status, Bundle extras)
	{
		nativeLib.SetLocationStatus( status );
	}
		
		
		
// View.OnTouchListener //
	
	// onTouch
	public final boolean onTouch (View vw, MotionEvent ev)
	{
		final int	ACTION_MOTION 	= 1;
		final int	ACTION_DOWN   	= 2;
		final int	ACTION_UP		= 4;
		
		if ( nativeLib == null )
			return false;
		
		int pointerCount	= ev.getPointerCount();
		int action 			= ev. getActionMasked();
		int actionBits 		= 0;
		
		switch ( action )
		{
			case MotionEvent.ACTION_MOVE :
				actionBits = ACTION_MOTION;
				break;
				
			case MotionEvent.ACTION_DOWN :
				actionBits = ACTION_DOWN;
				break;
			
			case MotionEvent.ACTION_UP   : 
				actionBits = ACTION_UP;
				break;
		}

		if ( actionBits == ACTION_MOTION && pointerCount > 1 )
		{
			int j = 0;
			
			pointerCount = Math.min( pointerCount, MAX_TOUCHES );
			
			for (int i = 0; i < pointerCount; ++i)
			{
				_touchesData[j++] = (float) ev.getPointerId(i);
				_touchesData[j++] = ev.getX(i);
				_touchesData[j++] = _height - ev.getY(i);
				_touchesData[j++] = ev.getPressure(i);
			}
        }
		else
		{
			int actionIndex = ev.getActionIndex();
			
    		_touchesData[0] = (float) ev.getPointerId( actionIndex );
			_touchesData[1] = ev.getX();
			_touchesData[2] = _height - ev.getY();
			_touchesData[3] = ev.getPressure();
			
        	pointerCount = 1;
		}
		
		nativeLib.OnTouch( actionBits, pointerCount, _touchesData );
		return true;
	}


		
// View.OnKeyListener //
	
	// onKey
	public final boolean onKey (View v, int keyCode, KeyEvent ev)
	{
		if ( nativeLib == null )
			return false;
		
		if ( keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
			 keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
			 keyCode == KeyEvent.KEYCODE_VOLUME_MUTE )
		{
			// android will change volume
			return false;
		}
		
		if ( ev.getAction() == KeyEvent.ACTION_DOWN )
		{
			nativeLib.OnKey( keyCode, true );
			return true;
		}
		else
		if ( ev.getAction() == KeyEvent.ACTION_UP )
		{
			nativeLib.OnKey( keyCode, false );
			return true;
		}    
		return false;
	}
		
		
		
// Runnable //

	// run
	public final void run ()
	{
		if ( nativeLib == null )
			return;
		
		final int delay = nativeLib.ProcessMessages();
		
		if ( _handler != null )
		{
			_handler.postDelayed( this, delay );
		}
	}
		
		
		
// Private Utils //
	
	// _GetSupportedSensorsBits
	private final int _GetSupportedSensorsBits ()
	{
		List<Sensor>	sensors = _sensorManager.getSensorList( Sensor.TYPE_ALL );
		int				bits	= 0;
		
		for (int i = 0; i < sensors.size(); ++i)
		{
			bits |= 1 << _ConvertASensorType( sensors.get(i).getType() );
		}
		return bits;
	}
	
	/*
	// _GetAvailableMemoryMb
	protected final int _GetAvailableMemoryMb ()
	{
		MemoryInfo mi = new MemoryInfo();
    	ActivityManager activityManager = (ActivityManager) _context.getSystemService( Context.ACTIVITY_SERVICE );
    	activityManager.getMemoryInfo( mi );
		return (int)( mi.availMem >> 20 );
	}*/
	  

	// _SendDeviceInfo
	private final void _SendDeviceInfo ()
	{
		Runtime rt = Runtime.getRuntime();
		
		String		android_id 	= Secure.getString( _context.getContentResolver(), Secure.ANDROID_ID );
		long		total_mem	= 0;
		int  		freq 		= 0;
		InputStream in 	 		= null;
		
		try {
			final Process process = new ProcessBuilder( new String[] { "/system/bin/cat",
						"/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" }).start();

			in = process.getInputStream();
			
			final StringBuilder sb = new StringBuilder();
			final Scanner sc = new Scanner( in );
			while ( sc.hasNextLine() ) {
				sb.append( sc.nextLine() );
			}
			final String content = sb.toString();
			 
			
			freq = Integer.parseInt( content );
		}
		catch (final Exception e) {
			Logger.OnCatchException( TAG, e );
		}
		finally {
			if ( in != null )
			{
				try {
					in.close();
				} catch (Exception e)
				{}
			}
		}

    	WindowManager wm   =  (WindowManager) _context.getSystemService( Context.WINDOW_SERVICE );
    	
		DisplayMetrics metrics = new DisplayMetrics();
		Display display = wm.getDefaultDisplay();
		display.getMetrics( metrics );


		if ( UXUtils.CheckAPIVersion( 16 ) )
		{
			MemoryInfo mi = new MemoryInfo();
	    	ActivityManager activityManager = (ActivityManager) _context.getSystemService( Context.ACTIVITY_SERVICE );
	    	activityManager.getMemoryInfo( mi );
			total_mem = mi.totalMem;
		}
		else
		{
		    String 		str1 = "/proc/meminfo";
		    String 		str2;        
		    String[] 	arrayOfString;

	    	total_mem = 0;
	    	
		    try {
			    FileReader 		localFileReader 	= new FileReader( str1 );
			    BufferedReader 	localBufferedReader	= new BufferedReader( localFileReader, 8192 );
			    
			    str2 			= localBufferedReader.readLine();
			    arrayOfString 	= str2.split( "\\s+" );
			    
			    total_mem = Integer.valueOf( arrayOfString[1] ).intValue() * 1024;   
			    localBufferedReader.close();
		    } 
		    catch (Exception e) 
		    {
		    	Logger.OnCatchException( TAG, e );
		    }
		}
		
		nativeLib.SetDeviceInfo(
				android.os.Build.MANUFACTURER + " " + android.os.Build.MODEL,
				android.os.Build.VERSION.RELEASE,
				android_id,
				UXApplication.GetPackageName(),
				_context.getResources().getConfiguration().locale.getLanguage(),
				rt.availableProcessors(),
				freq >> 10,
				total_mem,
				UXUtils.GetAPIVersion(),
				metrics.widthPixels,
				metrics.heightPixels,
				metrics.densityDpi,
				display.getRotation()
			);
	}
	 
	
	
// Public Utils //
	
	// GetContext
	public final Context GetContext ()
	{
		return _context;
	}
	
	
	// UpdateDisplayOrientation
	public final void UpdateDisplayOrientation ()
	{
		final Display display = 
				((WindowManager) _context.getSystemService( Context.WINDOW_SERVICE )).getDefaultDisplay();
		
		nativeLib.OnDisplayOrientationChanged( display.getRotation() );
	}
	
	
	// IsNetworkConnected
	public final boolean IsNetworkConnected ()
	{
		// #ifdef INTERNET
		try {
		    ConnectivityManager cm = (ConnectivityManager) _context.getSystemService( Context.CONNECTIVITY_SERVICE );
		    NetworkInfo 		ni = cm.getActiveNetworkInfo();
		    
		    if ( ni != null )
		    {
		    	DetailedState state = ni.getDetailedState();
		    	
		    	if ( state == DetailedState.CONNECTED )
		    		return true;
		    }
		}
		catch (Exception e)
		{}
	    return false;
		/* #else  INTERNET
		Logger.W( TAG, "IsNetworkConnected not supported" );
		return false;
		#endif INTERNET */
	}

    
    // IsVisible
    public final boolean IsVisible ()
    {
    	return _isVisible;
    }
	
    
	// ShowNotification
	public final void ShowNotification (String text, boolean longTime)
	{
		int duration = longTime ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT;
		Toast toast = Toast.makeText( _context, text, duration );
		toast.show();
	}
	
	
	// GetView
	public final View GetView (int viewId)
	{
		return _rootView == null ? null : _rootView.findViewById( viewId );
	}
	
	
	// GetRootView
	public final View GetRootView ()
	{
		return _rootView;
	}
	
	
	// GetOpenGLSurfaceView
	public final SurfaceView GetOpenGLSurfaceView ()
	{
		return _view;
	}
	
	
	//
	// Show Message Dialog Interface
	//
	private final static class DialogResult implements DialogInterface.OnClickListener
	{
		private final int		_msgID;
		private final int		_result;
		
		public DialogResult (int msgID, int result)
		{
			_msgID	= msgID;
			_result	= result;
		}

		public final void onClick (DialogInterface arg0, int arg1)
		{
			NativeLib.MessageResult( _msgID, _result );
		}
	}
	
	
	// ShowMessage
	public final void ShowMessage (int msgID, String caption, String text, String positive, String negative)
	{
		AlertDialog.Builder builder = new AlertDialog.Builder( _context );
		
		builder.setMessage( text );
		builder.setTitle( caption );
		builder.setCancelable( false );
		
		if ( positive.length() > 0 )
		{
			builder.setPositiveButton( positive, new DialogResult( msgID, 1 ) );
		}
	
		if ( negative.length() > 0 )
		{
			builder.setNegativeButton( negative, new DialogResult( msgID, -1 ) );
		}

		builder.create().show();
	}
	
	
	// SendEmail
	public final void SendEmail (String address, String subject, String text, String attachment)
	{
		// #ifdef INTERNET
		Intent i = new Intent( Intent.ACTION_SEND );
	    i.setType( "text/plain" );
	    
	    i.putExtra( Intent.EXTRA_EMAIL,   new String[]{ address } );
		i.putExtra( Intent.EXTRA_SUBJECT, subject );
		i.putExtra( Intent.EXTRA_TEXT,    text );
		
		final File file = new File( attachment );
		
		if ( !attachment.isEmpty() && file.exists() && file.isFile() )
			i.putExtra( Intent.EXTRA_STREAM,  Uri.fromFile( file ) );
		
		try {
			_context.startActivity( Intent.createChooser( i, _context.getString( R.string.email_send_via ) ) );
		}
		catch (Exception e) {
			Logger.OnCatchException( TAG, e );
		}
		/* #else  INTERNET
		Logger.W( TAG, "SendEmail not supported" );
		#endif INTERNET */
	}
	
	/*
	// BackgroundMode
	public final void BackgroundMode (boolean enabled)
	{
		_backgroundModeEnabled = enabled;
		
		if ( _backgroundModeEnabled && _handler == null )
		{
			_handler = new Handler();
			_handler.post( this );
		}
	}
	*/
	
	// RequestAudioFocus
	public final void RequestAudioFocus ()
	{
		final int	SET_FOCUS	= 1;
		final int	KILL_FOCUS	= 2;
		final int	PAUSED		= 4;
		
		if ( nativeLib == null || _audioFocusListener != null )
			return;
		
		_audioFocusListener = new AudioManager.OnAudioFocusChangeListener()
		{ 
			public void onAudioFocusChange (int focusChange)
			{
				switch ( focusChange )
			  	{
			  		case AudioManager.AUDIOFOCUS_GAIN:
						nativeLib.AudioFocusChanged( SET_FOCUS );
			  			break;
			  			
			  		case AudioManager.AUDIOFOCUS_LOSS:
						nativeLib.AudioFocusChanged( KILL_FOCUS );
			  			break;
			  			
			  		case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
			  		case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
						nativeLib.AudioFocusChanged( KILL_FOCUS | PAUSED );
			  			break;
			  	}
			}
		};
		
		AudioManager am = (AudioManager) _context.getSystemService( Context.AUDIO_SERVICE );
		
		int result = am.requestAudioFocus( _audioFocusListener,
											AudioManager.STREAM_MUSIC,
											AudioManager.AUDIOFOCUS_GAIN );
		
		if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED)
			nativeLib.AudioFocusChanged( SET_FOCUS );
		else
		if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED)
			nativeLib.AudioFocusChanged( KILL_FOCUS );
	}
	
	
	// AbandonAudioFocus
	public final void AbandonAudioFocus ()
	{
		if ( _audioFocusListener == null )
			return;

		AudioManager am = (AudioManager) _context.getSystemService( Context.AUDIO_SERVICE ); 
		am.abandonAudioFocus( _audioFocusListener );
		
		_audioFocusListener = null;
	}
	
	
	// Vibrate
	public final void Vibrate (long[] pattern)
	{
		// #ifdef VIBRATE
		Vibrator vb = (Vibrator) _context.getSystemService( Context.VIBRATOR_SERVICE );
		vb.vibrate( pattern, -1 );
		/* #else  VIBRATE
		Logger.W( TAG, "Vibrate not supported" );
		#endif VIBRATE */
	}
	
	
	// OpenURL
	public final void OpenURL (String url)
	{
		// #ifdef INTERNET
		if ( !url.startsWith( "http://" ) &&
			 !url.startsWith( "https://" ) &&
			 !url.startsWith( "market://" ) &&
			 !url.startsWith( "file:///" ) )
		{
			url = "http://" + url;
		}
		Intent browserIntent = new Intent( Intent.ACTION_VIEW, Uri.parse(url) );
		_context.startActivity( browserIntent );
		/* #else INTERNET
		Logger.W( TAG, "OpenURL not supported" );
		#endif INTERNET */
	}
	
	
	// OpenMarket
	public final void OpenMarket (String name)
	{
		OpenURL( "https://play.google.com/store/apps/details?id=" + name );
		//OpenURL( "market://details?id=" + name );
	}
	
	
	// OpenUrlFromAsset
	public final void OpenUrlFromAsset (String name)
	{
		OpenURL( "file:///android_asset/" + name );
	}
	
	
	// EnableWiFi
	public final void EnableWiFi (boolean enable)
	{
		// #ifdef WIFI_ACCESS
		WifiManager	wifiMngr = (WifiManager) _context.getSystemService( Context.WIFI_SERVICE );
		int			state 	 = wifiMngr.getWifiState();
		
		if ( enable && state != WifiManager.WIFI_STATE_ENABLED && state != WifiManager.WIFI_STATE_ENABLING ) {
			wifiMngr.setWifiEnabled( true );
		}
		else
		if ( !enable && state == WifiManager.WIFI_STATE_ENABLED && state == WifiManager.WIFI_STATE_ENABLING ) {
			wifiMngr.setWifiEnabled( false );
		}
		/* #else WIFI_ACCESS
		Logger.W( TAG, "EnableWiFi not supported" );
		#endif WIFI_ACCESS */
	}
	
	
	// SetDisplayOrientation
	public final void SetDisplayOrientation (int orientation)
	{
		final int DEFAULT			= -1;
		final int SENSOR			= -2;
		final int PORTRAIT			= 0;
		final int PORTRAIT_REVERSE	= 180;
		final int LANDSCAPE			= 90;
		final int LANDSCAPE_REVERSE	= 270;
		
		int new_orient = 0;
		
		switch ( orientation )
		{
			default:
			case DEFAULT:			new_orient = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;		break;
			case SENSOR:			new_orient = ActivityInfo.SCREEN_ORIENTATION_SENSOR;			break;
			case PORTRAIT:			new_orient = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;			break;
			case PORTRAIT_REVERSE:	new_orient = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;	break;
			case LANDSCAPE:			new_orient = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;			break;
			case LANDSCAPE_REVERSE:	new_orient = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;	break;
		}
		
		_activityInterface.SetRequestedOrientation( new_orient );
	}
	
	
	// SendUserEvent
	public final void SendEngineEvent (int event, int param0, int param1, int param2)
	{
		if ( nativeLib != null ) {
			nativeLib.SendEngineEvent( event, param0, param1, param2 );
		}
	}
	
	
	// ProcessEngineEvent
	public final void ProcessEngineEvent (int event, int param0, int param1, int param2)
	{
		for (EventListener el : _listeners) {
			el.OnProcessEvent( event, param0, param1, param2 );
		}
	}
	
	
	// RegisterEventListener
	public final void RegisterEventListener (EventListener el)
	{
		_listeners.add( el );
	}
	
	
	// UnRegisterEventListener
	public final void UnRegisterEventListener (EventListener el)
	{
		_listeners.remove( el );
	}
	
	
	// EnableExtension
	public final void EnableExtension (int ext, boolean enable)
	{
		switch ( ext )
		{
			case EXT_CAMERA :
				// #ifdef CAMERA
				if (  enable && camera == null ) {
					camera = new uxgenes.ext.camera.UXCamera( this, R.id.cameraPreview );
					AddExtension( camera );
				}
				else
				if ( !enable && camera != null ) {
					RemoveExtension( camera );
					camera = null;
				}
				/* #else CAMERA
				Logger.W( TAG, "Camera not supported" );
				// #endif CAMERA */
				return;
				
			default :
				Logger.W( TAG, "Unknown extension " + ext );
				return;
		}
	}
	
	
	// AddExtension
	public final void AddExtension (uxgenes.ext.IExtension ext)
	{
		if ( ext == null ) {
			Logger.E( TAG, "added extension is null!" );
			return;
		}
		_extensions.add( ext );
		ext.OnAdded( this );
	}
	
	
	// RemoveExtension
	public final void RemoveExtension (uxgenes.ext.IExtension ext)
	{
		_extensions.remove( ext );
	}
}
